package stone926.mods.more_enchantments.mixins;

import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.*;
import net.minecraft.entity.mob.AbstractSkeletonEntity;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.entity.projectile.ProjectileUtil;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.RangedWeaponItem;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Hand;
import net.minecraft.world.LocalDifficulty;
import net.minecraft.world.ServerWorldAccess;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;
import stone926.mods.more_enchantments.interfaces.IEntitySpawnedByFlower;
import stone926.mods.more_enchantments.interfaces.IPersistentProjectileEntity;
import stone926.mods.more_enchantments.items.bow.StoneBow;

@Mixin(AbstractSkeletonEntity.class)
public abstract class AbstractSkeletonEntityMixin extends HostileEntity {

  protected AbstractSkeletonEntityMixin(EntityType<? extends HostileEntity> entityType, World world) {
    super(entityType, world);
  }

  @Shadow
  @Nullable
  public abstract EntityData initialize(ServerWorldAccess world, LocalDifficulty difficulty, SpawnReason spawnReason, @Nullable EntityData entityData, @Nullable NbtCompound entityNbt);

  @Redirect(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/AbstractSkeletonEntity;setOnFireFor(I)V"))
  private void redirectSetOnFireFor(AbstractSkeletonEntity abstractSkeletonEntity, int seconds) {
    if (!((IEntitySpawnedByFlower) abstractSkeletonEntity).avoidBurning())
      abstractSkeletonEntity.setOnFireFor(seconds);
  }

  @Inject(method = "canUseRangedWeapon", at = @At("RETURN"), cancellable = true)
  private void canUseRangedWeapon(RangedWeaponItem weapon, CallbackInfoReturnable<Boolean> cir) {
    cir.setReturnValue(
      cir.getReturnValueZ()
        || weapon == MoreEnchantmentsMod.WITHER_SKULL_BOW
        || weapon == MoreEnchantmentsMod.SHULKER_BOW
        || weapon == MoreEnchantmentsMod.FIREBALL_BOW
    );
  }

  @Inject(method = "attack", at = @At("HEAD"), cancellable = true)
  private void attack(LivingEntity target, float pullProgress, CallbackInfo ci) {
    ItemStack mainHandStack = getMainHandStack();
    if (mainHandStack.getItem() instanceof StoneBow bow) {
      bow.mobUse(this, target, mainHandStack, pullProgress, target.getActiveItem());
      this.playSound(SoundEvents.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));
      ci.cancel();
    }
  }

  @Redirect(method = "attack", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"))
  private boolean traction(World instance, Entity entity) {
    PersistentProjectileEntity projectile = (PersistentProjectileEntity) entity;
    ((IPersistentProjectileEntity) projectile).setTraction(EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.TRACTION, (LivingEntity) projectile
      .getOwner()));
    return instance.spawnEntity(projectile);
  }

  @Redirect(method = "updateAttackType", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/AbstractSkeletonEntity;getStackInHand(Lnet/minecraft/util/Hand;)Lnet/minecraft/item/ItemStack;"))
  private ItemStack updateAttackType(AbstractSkeletonEntity instance, Hand hand) {
    ItemStack bow = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, Items.BOW));
    if (bow.isEmpty()) {
      bow = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, MoreEnchantmentsMod.SHULKER_BOW));
    }
    if (bow.isEmpty()) {
      bow = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, MoreEnchantmentsMod.FIREBALL_BOW));
    }
    if (bow.isEmpty()) {
      bow = this.getStackInHand(ProjectileUtil.getHandPossiblyHolding(this, MoreEnchantmentsMod.WITHER_SKULL_BOW));
    }
    return bow;
  }

  @Redirect(method = "updateAttackType", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isOf(Lnet/minecraft/item/Item;)Z"))
  private boolean updateAttackType2(ItemStack stack, Item item) {
    return stack.isOf(item) || stack.getItem() instanceof StoneBow;
  }

}
